/***************************************************************************************************
 *Copyright(C),2010-2016,Sumscope
 *FileName	:  DictLoadListener.cpp
 *Author	:  scofined.qi
 *Version	:  1.0
 *Date		:  2017/04/10
 *Desc		:  从二进制映射文件的字典文件中读取数据,更新到各自的内存容器中
 *Relation :
 *Others	:
 *Function :
 *History	:
 ***************************************************************************************************/
#include "DictLoadListener.h"

#include <core/strings/pinyin.h>
#include <core/strings/pinyin_search.h>
#include <core/strings/string_util.h>

#include "DictType.h"
// #include "../../SSTools/ConfigVM.h"
#include <bondlib/ABSBondInfo.h>
#include <bondlib/AdditionBondInfo.h>
#include <bondlib/BondAuctionInfo.h>
#include <bondlib/BondCalendar.h>
#include <bondlib/BondContainer.h>
#include <bondlib/BondDict.h>
#include <bondlib/BondField/CBondInfoImpl.h>
#include <bondlib/BondInfo.h>
#include <bondlib/BondOptionInfo.h>
#include <bondlib/BondSpecialLabel.h>
#include <bondlib/CCFlowInfo.h>
#include <bondlib/ExchangeRepoInfo.h>
#include <bondlib/Pinyin.h>
#include <bondlib/QBBondCrmClause.h>
#include <bondlib/QBCategoryFlagInfo.h>
#include <bondlib/QBIssuerInfo.h>
#include <bondlib/QBProviceInfo.h>
#include <bondlib/QBSwSectorInfo.h>
#include <bondlib/SSFinancialCompany.h>
#include <bondlib/UnderwriterInfo.h>
#include <uam/ServerTimeMgr.h>

typedef qb::base::SpellSearch SpellSearch;
#ifdef ENABLE_TIME_METRIC
#define TIME_METRIC_DECLARE() ss::base::TimeRangeMetric _tm_metric;
#define TIME_METRIC_RESET() _tm_metric.Reset();
#define TIME_METRIC_SET_METRICS(STEPS) \
  _tm_metric.SetMetrics(STEPS, _countf(STEPS));
#define TIME_METRIC_ADD_TIME(INDEX) _tm_metric.AddTime(INDEX);
#else
#define TIME_METRIC_DECLARE()
#define TIME_METRIC_RESET()
#define TIME_METRIC_SET_METRICS(STEPS)
#define TIME_METRIC_ADD_TIME(INDEX)
#endif

/*
        CBondInfoDebug/CBondInfoImpl与CBondInfoOld的测试的条件:
        1.宏USE_BOND_OLD没有定义
        2.宏USE_BONDINFO_CHECKQUAL已经定义
        3.配置变量kProfileCheckBondInfoEqual值设置为true(默认为false)
*/
#define USE_BONDINFO_CHECKQUAL
#ifndef USE_BOND_OLD  // 控制CBondInfo的具体定义,见BondInfo.h文件
#ifdef USE_BONDINFO_CHECKQUAL
#define CHECK_BOND_LOG(NUM) \
  BLOGDEBUG("[BONDCHECK]started,待检查记录个数:%d", NUM)
#define CHECK_BOND_EQUAL(BL, IMPL, OLD, KEY, CODE, NAME) \
  OLD->Clear();                                          \
  ConvertWithBondList<>(BL, *OLD, KEY, CODE);            \
  OLD->SetBondShortName(NAME);                           \
  CBondInfo::CheckEqual(IMPL, OLD);
#else
#define CHECK_BOND_EQUAL(BL, IMPL, OLD, KEY, CODE, NAME)
#define CHECK_BOND_LOG()
#endif
#endif  // USE_BOND_OLD

#define RTL_FIELD_SIZE(type, field) (sizeof(((type*)0)->field))

namespace qb {
namespace ht {
#define CHECK_FIELD_SIZE(DESTTYPE, FIELD1, SRCTYPE, FIELD2)                \
  static_assert(                                                           \
      RTL_FIELD_SIZE(DESTTYPE, FIELD1) == RTL_FIELD_SIZE(SRCTYPE, FIELD2), \
      #DESTTYPE "." #FIELD1 "!=" #SRCTYPE "." #FIELD2);

#define COPY_FIELD(DESTTYPE, DEST, FIELD1, SRCTYPE, SRC, FIELD2) \
  CHECK_FIELD_SIZE(DESTTYPE, FIELD1, SRCTYPE, FIELD2);           \
  memcpy(DEST.FIELD1, SRC.FIELD2, RTL_FIELD_SIZE(SRCTYPE, FIELD2));

bool IsEn = false;
bool IsFlagDelete(char Flag) {
  return (bool)Flag;
  // return Flag == 'D';
}
bool IsFlagUpdate(char Flag) {
  return !((bool)Flag);
  // return Flag == 'U' || Flag == 'I';
}

template <typename TYPE>
bool CheckMemberEqual(TYPE t1, TYPE t2) {
  return t1 == t2;
}
template <>
bool CheckMemberEqual(const char* t1, const char* t2) {
  return strcmp(t1, t2) == 0;
}
RecordEnumerator::RecordEnumerator(DictTable* table, bool all)
    : m_table(table), m_all(all), m_end(this, -1) {
  BindTable(table, all);
}

void RecordEnumerator::BindTable(DictTable* table, bool all) {
  m_indexes.clear();
  m_count = table->GetMaxRecordId() + 1;
  if (!all) {
    char keybuf[1024] = {0};
    int key_length = 128;  // 可以动态计算出来;
    BASSERT(key_length < _countof(keybuf));
    int resetsize = key_length + 1;
    const DictTable::RecordStateMap& sets =
        table->GetLastUpdateSet().GetStateMap();
    m_indexes.reserve(sets.size());
    for (auto it = sets.begin(); it != sets.end(); ++it) {
      const std::string& key = it->first;
      const DictTable::RecordState& rs = it->second;
      memset(keybuf, 0, resetsize);
      memcpy(keybuf, key.c_str(), key.size());

      // 当keybuf对应的记录被删除,之后再次更新时,其索引将会变化
      // 即出现ri.rid != ri.state.rid的情况
      // 此时以ri.rid为准
      RecordIndex ri;
      ri.rid = table->GetRecordId(keybuf);
      ri.state = rs;  // 将record临时拷贝指针
      if (ri.rid < 0) continue;
      m_indexes.push_back(ri);
    }
    m_count = m_indexes.size();
  } else {
    table->GetAllRecord(m_records);
    std::sort(m_records.begin(), m_records.end());
    m_count = m_records.size();
  }
  m_end = Iterator(this, m_count);
}
bool RecordEnumerator::IsEmpty() const {
  if (m_all) {
    return m_table->GetRecordCount() <= 0 ? true : false;
  }
  return m_indexes.size() <= 0 ? true : false;
}
const char* RecordEnumerator::ReadRecord(int index) {
  const char* data = NULL;
  if (m_all) {
    data = m_table->ReadRecord(m_records[index]);
  } else if (index >= 0 && index < (int)m_indexes.size()) {
    const RecordIndex& rindex = m_indexes[index];
    if (rindex.state.removed && rindex.state.record) return rindex.state.record;
    if (rindex.rid < 0) return nullptr;
    data = m_table->ReadRecord(rindex.rid);
    if (data == NULL) {
      kDictType type = m_table ? m_table->GetType() : kDictTypeCount;
      if (type != kDictTypeCount) {
        const std::string& name = DictDesc::GetStockDictDesc(type).GetName();
        log_error("[DICT]invalid record at index:%d-%d for dict:%s", rindex.rid,
                  rindex.state.rid, name.c_str());
      } else
        log_error("[DICT]invalid record at index:%d-%d for dict:%s", rindex.rid,
                  rindex.state.rid, "None");
    }
  }
  return data;
}
template <typename BONDINFO>
inline void SetBondInfoKeyAndMarket(BONDINFO& bi, const char* combBondKey,
                                    int capcity) {
  // 原先的SplitCombBondKey存在少量crash问题,此处改动提升性能
  static_assert(64 > RTL_FIELD_SIZE(ST_BOND_LIST, ComBondKey) &&
                    64 > RTL_FIELD_SIZE(BOND_PRIMARY_INFO, ComBondKey),
                "bondkey尺寸过小");
  char bondkey[64] = {0};
  if (combBondKey == 0 || *combBondKey == '\0') {
    bi.SetBondKey("");
    bi.SetListedMarket("");
  } else {
    memcpy(bondkey, combBondKey, capcity);
    bondkey[capcity] = '\0';
    const char* dot = strrchr(bondkey, '.');
    if (dot) {
      bondkey[(int)(dot - bondkey)] = '\0';
      dot++;
    }
    bi.SetBondKey(bondkey);
    bi.SetListedMarket(dot ? dot : "");
  }
}
//////////////////////////////////////////////////////////////////////////
class KeyBuffer {  // see CSSVContainer::SetMap( )
  enum {
    KEYSIZE = sizeof(tBondKey) + sizeof(tListedMarket),
    BUFSIZE = KEYSIZE + 12
  };
  /*
  当m_code为true的时候,生成combcode,将listedMarket转化为IB,SH,SZ
  当m_code为false的时候,生成combkey,将listedMarket直接追加到bondkey末尾
  两者都需要用.隔开
  */
  bool m_code;
  char m_buf[BUFSIZE];

 public:
  KeyBuffer(bool code) : m_code(code) { memset(m_buf, 0, BUFSIZE); }
  const char* MakeImpl(const char* bondKey, int bn, const char* listedMarket) {
    const char* market = m_code ? GetKeyMarket(listedMarket) : listedMarket;
    bn = bn < KEYSIZE ? bn : KEYSIZE;
    int mn = bn <= 0 ? 0 : (strlen(market));
    mn = mn < (KEYSIZE - bn - 1) ? mn : (KEYSIZE - bn - 1);
    if (bn > 0) memcpy(m_buf, bondKey, bn);
    if (mn > 0) {
      m_buf[bn] = '.';
      memcpy(m_buf + bn + 1, market, mn);
      m_buf[mn + bn + 1] = 0;
    } else
      m_buf[bn] = 0;
    return m_buf;
  }
  const char* Make(const char* bondKey, const char* listedMarket) {
    return MakeImpl(bondKey, strlen(bondKey), listedMarket);
  }
  const char* FixMarket(const char* combBondKey, const char* listMarket) {
    // 如果combBondKey中包含市场如.CIB,那么将其市场替换成指定的
    const char* dot = strchr(combBondKey, '.');
    if (dot == NULL) {
      return MakeImpl(combBondKey, strlen(combBondKey), listMarket);
    }
    return combBondKey;
  }
  const char* GetKeyMarket(const char* listedMarket) const {
    static const char* strs[] = {"IB", "SH", "SZ"};
    if (strcmp(listedMarket, CBondContainer::lmSSE) == 0) {
      return strs[1];
    } else if (strcmp(listedMarket, CBondContainer::lmSZE) == 0) {
      return strs[2];
    }
    return strs[0];
  }
};
void ConvertWithPrimaryInfo(const BOND_PRIMARY_INFO& bl, CBondInfo& bi,
                            KeyBuffer& key, KeyBuffer& code) {
  CBondInfo* pInfo = &bi;
  SetBondInfoKeyAndMarket(bi, bl.ComBondKey,
                          RTL_FIELD_SIZE(BOND_PRIMARY_INFO, ComBondKey));
  pInfo->SetCombBondKey(bl.ComBondKey);

  pInfo->SetPlannedIssueAmount(bl.PlannedIssueAmount);
  pInfo->SetActualIssueAmount(bl.Actual_Issue_Amount);
  pInfo->SetPaymentDate(bl.PaymentDate);
  pInfo->SetCouponRateCurrent(bl.CouponRateCurrent);
  pInfo->SetAuctionDateStart(bl.AuctionDateStart);
  pInfo->SetAuctionDateEnd(bl.AuctionDateEnd);
  pInfo->SetAuctionDateTime(bl.Auction_Date_Time);
  pInfo->SetAnnounceDate(bl.AnnounceDate);
  pInfo->SetIssueStartDate(bl.IssueStartDate);
  pInfo->SetIssueEndDate(bl.IssueEndDate);
  pInfo->SetIssueEndTime(bl.Issue_End_Time);
  pInfo->SetIssuePeriod(bl.Issue_Period);
  pInfo->SetUnderwrites(bl.Underwriters);
  pInfo->SetBidLimitBottom(bl.Bid_Limit_Bottom);
  pInfo->SetBidLimitTop(bl.Bid_Limit_Top);
  pInfo->SetAuctionFlr(bl.Auction_Flr);
  pInfo->SetAuctionCap(bl.Auction_Cap);
  pInfo->SetAuctionStep(bl.Auction_Step);
  pInfo->SetAuctionUnit(bl.Auction_Unit);
  pInfo->SetMainUnderwriters(bl.MainUnderwriters);
  pInfo->SetJointUnderwriters(bl.JointUnderwriters);
  pInfo->SetBondFileNum(bl.BondFileNum);
  pInfo->SetAutcionType(bl.Auction_Type);
  pInfo->SetAuctionTimeStart(bl.Auction_Time_Start);
  pInfo->SetAuctionTimeEnd(bl.Auction_Time_End);
  pInfo->SetIssueType(bl.Issue_Type);
  pInfo->SetAssetStatus(bl.Asset_Status);
  pInfo->SetPrincipalPayType(bl.Type);
}

template <typename BONDINFO>
void ConvertWithBondList(const BOND_LIST& bl, BONDINFO& bi, KeyBuffer& key,
                         KeyBuffer& code) {
  BONDINFO* pInfo = &bi;
  SetBondInfoKeyAndMarket(bi, bl.ComBondKey,
                          RTL_FIELD_SIZE(BOND_LIST, ComBondKey));

  pInfo->SetCombBondKey(bl.ComBondKey);

  pInfo->SetBondCode(bl.BondID);
  pInfo->SetCombBondCode(
      code.Make(bl.BondID, pInfo->GetListedMarket()));  // 已经在如上设置了

  // pInfo->SetBondShortName(bl.ShortName);
  pInfo->SetBondSubType(bl.BondSubType);
  pInfo->SetWarrant(bl.Guarantee);
  pInfo->SetTermToMaturity(bl.TermToMaturity);
  pInfo->SetListDate(bl.ListDate);
  pInfo->SetMaturityDate(bl.MaturityDate);
  pInfo->SetOptionDate(bl.OptionDate);
  pInfo->SetIssuerRating(bl.IssuerRating);
  pInfo->SetBondRating(bl.BondRating);
  pInfo->SetInstitutionRating(
      bl.InstitutionRating);  // LocalDataUpdate.cpp中漏写
  pInfo->SetRateType(bl.RateType);
  pInfo->SetCouponType(bl.Coupon_Type);
  pInfo->SetMarketType(bl.MarketType);
  pInfo->SetInterestStartDate(bl.InterestStartDate);
  pInfo->SetMortgage(bl.IsMortgate);
  pInfo->SetCrossmarket(bl.IsCrossMarket);
  pInfo->SetInstitutionType(bl.InstitutionSubtype);
  pInfo->SetYieldCurveType(bl.YieldCurveType);
  pInfo->SetMunicipal(bl.IsMunicipal);
  pInfo->SetBondName(bl.BondFullName);
  pInfo->SetBondShortName(bl.ShortName);
  pInfo->SetMaturityTerm(bl.MaturityTerm);
  pInfo->SetOptionType(bl.OptionType);
  pInfo->SetIssuerCode(bl.Issuer_Code);
  if (IsEn)
  {
    pInfo->SetBondNameEn(bl.FullNameEN);
    pInfo->SetBondShortNameEn(bl.ShortNameEN);
  }
  pInfo->SetOutlook(bl.Outlook);
  pInfo->SetEntCor(bl.Ent_Cor);
  pInfo->SetCouponFrequency(bl.Coupon_Frequency);  // LocalDataUpdate.cpp中漏写
  pInfo->SetCompoundFrequency(
      bl.Compound_Frequency);  // LocalDataUpdate.cpp中漏写
  pInfo->SetPlannedExerciseDate(bl.Planned_Exercise_Date);
  pInfo->SetNextCoupon_Date(bl.Next_Coupon_Date);
  pInfo->SetIssueRate(bl.Issue_Rate);
  pInfo->SetFRNIndexID(bl.FRN_Index_ID);
  pInfo->SetFixingMADays(bl.Fixing_MA_Days);
  pInfo->SetOutstandingAmount(bl.Outstanding_Amount);
  pInfo->SetIssuerRatingInstitutionCode(bl.Issuer_Rating_Institution_Code);
  pInfo->SetInterestBasis(bl.Interest_Basis);
  pInfo->SetCBRRating(bl.IssuerRatingCBR);
  // 			static char sCBRRate[13];
  // 			{//中债资信评级，去空格
  // 				memset(sCBRRate, 0, sizeof(sCBRRate));
  // 				FIELDCOPY(sCBRRate, bl.IssuerRatingCBR);
  // 				int len = strlen(sCBRRate);
  // 				for (int pos = len - 1; pos >= 0; pos--)
  // 				{
  // 					if (sCBRRate[pos] == ' ')
  // 						sCBRRate[pos] = '\0';
  // 					else
  // 						break;
  // 				}
  // 			}
  // 			pInfo->SetCBRRating(sCBRRate);
  pInfo->SetChinaDCState(bl.OutlookCBR);
  pInfo->SetCDCRating(bl.CDC_Implied_Rating);
  pInfo->SetCSIRating(bl.CSI_Implied_Rating);
  pInfo->SetCurrency(bl.Currency);
  pInfo->SetWarranter(bl.Warranter);
  pInfo->SetBondTag(bl.bond_tag);
  pInfo->SetPayDate(bl.pay_date);
  pInfo->SetSceniority(bl.Sceniority);
  pInfo->SetCategoryFlag(bl.Category_flag);
}
class BondContainerLock {
  CBondContainer& m_container;

 public:
  BondContainerLock(CBondContainer& container) : m_container(container) {
    m_container.Lock();
  }
  ~BondContainerLock() { m_container.UnLock(); }
};
class SpellCollector {
  SpellSearch::Collector* m_collector;

 public:
  SpellCollector(bool build = false) : m_collector(NULL) {
    m_collector = new SpellSearch::Collector(build);
  }
  ~SpellCollector() {
    if (m_collector) {
      m_collector->Commit();
      delete m_collector;
      m_collector = NULL;
    }
  }
  void AddNameChange(int id, const char* oldName, const char* name) {
    if (m_collector)
      m_collector->AddNameChange(id, oldName, oldName ? strlen(oldName) : 0,
                                 name, name ? strlen(name) : 0);
  }
  void AddNameEnChange(int id, const char* oldName, const char* name) {
    if (m_collector)
      m_collector->AddNameEnChange(id, oldName, oldName ? strlen(oldName) : 0,
                                   name, name ? strlen(name) : 0);
  }
  void AddCodeChange(int id, const char* oldCode, const char* code) {
    if (m_collector)
      m_collector->AddCodeChange(id, oldCode, oldCode ? strlen(oldCode) : 0,
                                 code, code ? strlen(code) : 0);
  }
};

//////////////////////////////////////////////////////////////////////////
bool UpdateBondContainerWithBondList(RecordEnumerator& bond_list) {
  // CSumscopeDB::LoadCodetable
  /*
  测试结果表明:
  当使用ReadDirect时,读取记录的时间从2000ms缩减到12ms
  这是使用ReadDirect的日志记录:
          readtime 12ms 0.59 converttime 2043ms 95.45 addtime 84ms 3.96

  经过测试表明,拼音占据时间最长
  readtime	0011ms	00.58%
  converttime 0143ms	07.02%
  addtime		0078ms	03.86%
  pinyintime	1812ms	88.54%

  */
  qb::base::HighResTime ht;
  CBondInfo bi;
  KeyBuffer combkey(false);
  KeyBuffer combcode(true);

  SpellCollector collector;
  // 尽早释放 BondContainerLock _scoped_(cont);
  {
    CBondContainer& cont = CBondContainer::instance();
    BondContainerLock _scoped_(cont);
    const char* dictkey = DictDesc::GetDebugKey();
    // const char* expired_str = (strcmp(AfxGetLanguageID(), "EN") == 0) ?
    // "Expired" : "已到期"; time_t tDate =
    // AfxGetBondContainer().GetServerTime();
    if (bond_list.IsAllMode()) {
      CBondInfo* pBond = NULL;
      bool benchflag = false;  // BOOLTEST(kProfileBenchLog);
      int mem_pinyin = 0;
      const char* steps[] = {"read", "conver", "pinyin", "add"};
      TIME_METRIC_DECLARE();
      TIME_METRIC_SET_METRICS(steps);

      for (RecordEnumerator::Iterator it = bond_list.begin();
           it != bond_list.end(); ++it) {
        TIME_METRIC_RESET();
        const BOND_LIST* pbl = (const BOND_LIST*)it.GetRecord();
        if (pbl == NULL) continue;
        TIME_METRIC_ADD_TIME(0);
        const BOND_LIST& bl = *pbl;
        if (dictkey && strcmp(dictkey, bl.ComBondKey) == 0) {
          log_debug("[DICT][DEBUGKEY]flag:%d key:%s name:%s", bl.Flag,
                    bl.ComBondKey, bl.ShortName);
        }

        if (IsFlagDelete(bl.Flag)) continue;
        if (pBond == 0)
          pBond = new CBondInfo();
        else
          pBond->Clear();

        ConvertWithBondList<>(bl, *pBond, combkey, combcode);
        TIME_METRIC_ADD_TIME(1);

        pBond->SetBondShortName(bl.ShortName);
        if (benchflag) {
          mem_pinyin +=
              pBond->GetBondJianpin().size() + pBond->GetBondQuanpin().size();
          mem_pinyin += sizeof(string) * 2;
        }
        TIME_METRIC_ADD_TIME(2);
        // 					if (checkBondInfo)
        // 					{
        // 						CHECK_BOND_EQUAL(bl,
        // pBond, pold, combkey, combcode, bl.ShortName);
        // 					}
        if (pBond->IsValidBond()) {
          cont.Add(pBond, NULL);
          pBond = NULL;
        }
        TIME_METRIC_ADD_TIME(3);

        CQBIssuerInfo::GuarantorInstitution().m_filterHelper.AddKeys(
            bl.Warranter, "|");
      }
      if (pBond) delete pBond;

      if (benchflag) {
        log_debug("[DICT]pinyin memory:%d", mem_pinyin);
      }
      log_debug("[DICT]UpdateBondContainer IsAllMode use %dus",
                ht.elapsed().microsec());
      return true;
    }

    if (cont.GetSize() <= 0) {
      //	AfxGetAvailableBond().Clear();
      return false;
    }

    CQBCategoryFlagInfo& cfInfo = CQBCategoryFlagInfo::instance();
    QBCATEGORYFLAGINFO qCfInfo;
    for (RecordEnumerator::Iterator it = bond_list.begin();
         it != bond_list.end(); ++it) {
      const BOND_LIST* pbl = (const BOND_LIST*)it.GetRecord();
      if (pbl == NULL) continue;
      const BOND_LIST& bl = *pbl;
      if (IsFlagUpdate(bl.Flag)) {
        int nID = cont.GetBondIndex(bl.ComBondKey);
        CBondInfo* pInfo = (nID >= 0) ? &cont.ElementAtW(nID) : &bi;
        if (nID < 0) pInfo->Clear();

        if (nID >= 0) {
          collector.AddNameChange(nID, pInfo->GetBondShortName(), bl.ShortName);
          collector.AddNameEnChange(nID, pInfo->GetBondShortNameEn().c_str(),
                                    bl.ShortNameEN);
          collector.AddCodeChange(nID, pInfo->GetBondCode(), bl.BondID);
        }
        ConvertWithBondList<>(bl, *pInfo, combkey, combcode);
        pInfo->SetBondShortName(bl.ShortName);
        // 					if (checkBondInfo)
        // 					{
        // 						CHECK_BOND_EQUAL(bl,
        // pInfo, pold, combkey, combcode, bl.ShortName);
        // 					}
        if (nID < 0) {
          if (pInfo->IsValidBond()) {
            nID = cont.Add(*pInfo, NULL);
            if (nID >= 0) {
              collector.AddNameChange(nID, "", pInfo->GetBondShortName());
              collector.AddNameEnChange(nID, "",
                                        pInfo->GetBondShortNameEn().c_str());
              collector.AddCodeChange(nID, "", pInfo->GetBondCode());
            }
          } else {
            log_error("[SERVER_DATA_ERROR:INVALID_BOND]ComBondKey:%s bid:%d",
                      bl.ComBondKey, nID);
          }
        }

        if (nID >= 0) {
          auto categoryFlag = pInfo->GetCategoryFlag();
          if (strlen(categoryFlag) <= 0) continue;
          FIELDCOPY(qCfInfo.CF_Name, categoryFlag);
          cfInfo.UpdateCategoryFlgInfo(qCfInfo);
        }
      } else if (IsFlagDelete(bl.Flag) &&
                 bl.No_Add_Issue <= 0)  // 增续发券不删，add by lance 20160111
      {
        int nID = cont.GetBondIndex(bl.ComBondKey);
        if (nID >= 0) {
          const CBondInfo& pInfo = cont.ElementAtR(nID);
          collector.AddNameChange(nID, pInfo.GetBondShortName(), bl.ShortName);
          collector.AddNameEnChange(nID, pInfo.GetBondShortNameEn().c_str(),
                                    bl.ShortNameEN);
          collector.AddCodeChange(nID, pInfo.GetBondCode(), bl.BondID);
        }
        const char* sCombKey = combkey.FixMarket(bl.ComBondKey, "CIB");
        log_debug("[DICT]Remove Bond [%s]", sCombKey);
        cont.Remove(combkey.FixMarket(bl.ComBondKey, "CIB"), false);
      }
      CQBIssuerInfo::GuarantorInstitution().m_filterHelper.AddKeys(bl.Warranter,
                                                                   "|");
    }

    cfInfo.LoadDataAndAlphabetSort();
  }

  log_debug("[DICT]UpdateBondContainer use %dus", ht.elapsed().microsec());

  return true;
}
const char* strBondLib_Expired() {
  static const char strExpire_CHS[] = "已到期";
  static const char strExpire_EN[] = "Expired";
  //	if (strcmp(AfxGetLanguageID(), "EN") == 0)
  //		return strExpire_EN;
  //	else
  return strExpire_CHS;
}
bool UpdateBondContainerWithPrimaryInfo(RecordEnumerator& bondp_info) {
  CBondInfo bi;
  KeyBuffer combkey(false);
  KeyBuffer combcode(true);
  CBondContainer& cont = CBondContainer::instance();
  const char* expired_str = strBondLib_Expired(); /*"已到期"*/ /*"已到期"*/
  time_t tNow = ServerTimeMgr::instance().serverTime();
  BondContainerLock _scoped_(cont);
  if (bondp_info.IsAllMode()) {
    const char* dictkey = DictDesc::GetDebugKey();
    for (RecordEnumerator::Iterator it = bondp_info.begin();
         it != bondp_info.end(); ++it) {
      const BOND_PRIMARY_INFO* pbl = (const BOND_PRIMARY_INFO*)it.GetRecord();
      if (pbl == NULL) continue;
      const BOND_PRIMARY_INFO& bl = *pbl;
      if (dictkey && strcmp(dictkey, bl.ComBondKey) == 0) {
        log_debug("[DICT][DEBUGKEY]flag:%d key:%s", bl.Flag, bl.ComBondKey);
      }
      if (IsFlagDelete(bl.Flag)) continue;
      int id = -1;
      CBondInfo* pInfo = &bi;
      cont.GetBondInfoByCombKey(bl.ComBondKey, NULL, &id);
      if (id >= 0 && id < cont.GetSize()) {
        pInfo = &(cont.ElementAtW(id));
      }
      ConvertWithPrimaryInfo(bl, *pInfo, combkey, combcode);
      if (id < 0 && pInfo->IsValidBond()) {
        cont.Add(*pInfo, NULL);
      }
      bi.Clear();
    }
    return true;
  }
  if (cont.GetSize() <= 0) {
    //	AfxGetAvailableBond().Clear();
    return false;
  }
  for (RecordEnumerator::Iterator it = bondp_info.begin();
       it != bondp_info.end(); ++it) {
    const BOND_PRIMARY_INFO* pbl = (const BOND_PRIMARY_INFO*)it.GetRecord();
    if (pbl == NULL) continue;
    const BOND_PRIMARY_INFO& bl = *pbl;
    if (IsFlagUpdate(bl.Flag)) {
      int nID = cont.GetBondIndex(bl.ComBondKey);
      CBondInfo* pInfo = (nID >= 0) ? &cont.ElementAtW(nID) : &bi;
      if (nID < 0) pInfo->Clear();

      ConvertWithPrimaryInfo(bl, *pInfo, combkey, combcode);
      if (nID < 0) {
        if (pInfo->IsValidBond()) {
          nID = cont.Add(*pInfo, NULL);
        } else {
          //	log_error("[SERVER_DATA_ERROR:INVALID_BOND]:key:%s market:%s
          //code:%s  in update record:ComBondKey:%s bid:%d",
          //		pInfo->GetBondKey(), pInfo->GetListedMarket(),
          //pInfo->GetBondCode(), 		bl.ComBondKey,nID);
        }
      }
    } else if (IsFlagDelete(bl.Flag)) {
      const char* sCombKey = combkey.FixMarket(bl.ComBondKey, "CIB");
      //	log_debug("[DICT]Remove Bond [%s]", sCombKey);
      cont.Remove(combkey.FixMarket(bl.ComBondKey, "CIB"), false);
    }
  }

  return true;
}
void ConvertToBondInfo(const BOND_ADDITION_INFO& bl, const char* listMarket,
                       CBondInfo& bi, KeyBuffer& key, KeyBuffer& code,
                       bool clear) {
  CBondInfo* pInfo = &bi;
  if (clear) pInfo->Clear();
  pInfo->SetBondKey(bl.Add_Issue_Key);
  pInfo->SetListedMarket(listMarket);
  pInfo->SetBondCode(bl.Add_Issue_ID);
  pInfo->SetCombBondKey(key.Make(bl.Add_Issue_Key, listMarket));
  pInfo->SetCombBondCode(code.Make(bl.Add_Issue_ID, listMarket));

  pInfo->SetBondName(bl.Add_Full_Name);
  pInfo->SetAnnounceDate(bl.Add_Announce_Date);
  pInfo->SetAuctionDateStart(bl.Add_Auction_Date);
  pInfo->SetAuctionDateEnd(bl.Add_Auction_Date);
  pInfo->SetIssueStartDate(bl.Add_Issue_Start_Date);
  pInfo->SetIssueEndDate(bl.Add_Issue_End_Date);
  pInfo->SetPaymentDate(bl.Add_Payment_Date);
  pInfo->SetListDate(bl.Add_Listed_Date);
  pInfo->SetPlannedIssueAmount(bl.Add_Planned_Issue_Amount);
  pInfo->SetActualIssueAmount(bl.Add_Issue_Amount);
  pInfo->SetBondShortName(bl.Add_Issue_Short_Name);
  pInfo->SetMarketType(bl.Mkt_Type);
  pInfo->SetIssueRate(bl.Bid_Outcome);
  pInfo->SetAutcionType(bl.Auction_Type);
  pInfo->SetAuctionTimeStart(bl.Auction_Time_Start);
  pInfo->SetAuctionTimeEnd(bl.Auction_Time_End);
}
void ConvertToAddtitionInfo(const BOND_ADDITION_INFO& bl, const char* market,
                            CAdditionBondInfo& abi, KeyBuffer& key,
                            KeyBuffer& code) {
  abi.SetNoAddIssue(bl.No_Add_Issue);
  abi.SetAddDistDateStart(bl.Add_Dist_Date_Start);
  abi.SetAddDistDateEnd(bl.Add_Dist_Date_End);
  abi.SetAddIssueCommissionRate(bl.Add_Issue_Commission_Rate);
  abi.SetAddIssuePrice(bl.Add_Issue_Price);
  abi.SetAnnStatus(bl.Ann_Status);

  abi.SetBidOutcome(bl.Bid_Outcome);
  abi.SetAutcionType(bl.Auction_Type);
  abi.SetAuctionTimeStart(bl.Auction_Time_Start);
  abi.SetAuctionTimeEnd(bl.Auction_Time_End);
  abi.SetReason(bl.Reason);

  abi.SetFirstIssueKey(key.Make(bl.First_Issue_Key, market));
  abi.SetFirstIssueID(code.Make(bl.First_Issue_ID, market));
}
bool UpdateBondContainerWithAddtionInfo(RecordEnumerator& addissue_info) {
  // 参考CLocalDataUpdate::UpdateBondContainer 和LoadAddIssueTable
  // IsAllMode 为true,表示初次加载
  CAdditionBondInfo abi;
  KeyBuffer combkey(false);
  KeyBuffer combcode(true);
  const char* markets[4] = {CBondContainer::lmCIB, CBondContainer::lmSSE,
                            CBondContainer::lmSZE, CBondContainer::lmBSE};
  int nmarket = _countof(markets);
  CBondContainer& cont = CBondContainer::instance();
  BondContainerLock _scoped_(cont);
  if (addissue_info.IsAllMode()) {
    for (RecordEnumerator::Iterator it = addissue_info.begin();
         it != addissue_info.end(); ++it) {
      const BOND_ADDITION_INFO* pbl = (const BOND_ADDITION_INFO*)it.GetRecord();
      if (pbl == NULL) continue;
      const BOND_ADDITION_INFO& bl = *pbl;
      if (!IsFlagUpdate(bl.Flag)) continue;
      bool bvalid = false;
      for (int n = 0; n < nmarket; n++) {
        int id = cont.GetBondIndex(bl.Add_Issue_Key, markets[n]);
        if (id >= 0) {
          ConvertToAddtitionInfo(bl, markets[n], abi, combkey, combcode);
          cont.AddAdditionInfo(id, &abi);  // 更新增续发信息
        } else {
          id = cont.GetBondIndex(bl.First_Issue_Key, markets[n]);
          if (id < 0) continue;
          CBondInfo bi = cont.ElementAtR(id);
          ConvertToBondInfo(bl, markets[n], bi, combkey, combcode, false);
          ConvertToAddtitionInfo(bl, markets[n], abi, combkey, combcode);
          id = cont.Add(bi, &abi);
          bvalid = true;
        }
      }
    }
    return true;
  }
  if (cont.GetSize() <= 0) {
    //	AfxGetAvailableBond().Clear();
    return false;
  }
  for (RecordEnumerator::Iterator it = addissue_info.begin();
       it != addissue_info.end(); ++it) {
    const BOND_ADDITION_INFO* pbl = (const BOND_ADDITION_INFO*)it.GetRecord();
    if (pbl == NULL) continue;
    const BOND_ADDITION_INFO& bl = *pbl;
    if (IsFlagDelete(bl.Flag)) {
      for (int n = 0; n < nmarket; n++) {
        //	log_debug("[DICT]Remove Bond [%s.%s]", bl.Add_Issue_Key,
        //markets[n]);
        cont.Remove(bl.Add_Issue_Key, markets[n], true);
      }
    } else if (IsFlagUpdate(bl.Flag)) {
      bool bvalid = false;
      for (int n = 0; n < nmarket; n++) {
        int id = cont.GetBondIndex(bl.Add_Issue_Key, markets[n]);
        if (id >= 0) {
          CBondInfo& pInfo = cont.ElementAtW(id);
          pInfo.SetIssueRate(bl.Bid_Outcome);
          ConvertToAddtitionInfo(bl, markets[n], abi, combkey, combcode);
          cont.AddAdditionInfo(id, &abi);  // 更新增续发信息
          bvalid = true;
        } else {
          id = cont.GetBondIndex(bl.First_Issue_Key, markets[n]);
          if (id < 0) continue;
          CBondInfo bi = cont.ElementAtR(id);
          ConvertToBondInfo(bl, markets[n], bi, combkey, combcode, false);
          ConvertToAddtitionInfo(bl, markets[n], abi, combkey, combcode);
          id = cont.Add(bi, &abi);
          bvalid = true;
        }
      }
      if (!bvalid) {
        // 原券信息缺失，增续发券无法依靠，认定为错误数据
        //	log_error("No First Issue Info, %s, %s", bl.Add_Issue_ID,
        //bl.Add_Full_Name);
      }
    }
  }
  return true;
}
bool UpdateBondOptionInfo(RecordEnumerator& fcmap) {
  CBondContainer& cont = CBondContainer::instance();
  BondContainerLock _scoped_(cont);
  // 			time_t tNow = cont.GetServerTime();
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const BOND_OPTION_INFO* opi = (const BOND_OPTION_INFO*)it.GetRecord();
    if (opi == NULL) continue;
    CBondOptionInfo info;
    info.SetCombBondKey(opi->ComBondKey);
    info.SetOptionType_(opi->OptionType);
    info.SetCallStr(opi->CallStr);
    info.SetPutStr(opi->PutStr);
    if (IsFlagDelete(opi->Flag)) {
      cont.RemoveBondOptionInfo(&info);
      continue;
    }
    cont.AddBondOptionInfo(&info);
  }
  return true;
}
bool UpdateBondAuctionInfo(RecordEnumerator& fcmap) {
  const char* markets[4] = {CBondContainer::lmCIB, CBondContainer::lmSSE,
                            CBondContainer::lmSZE, CBondContainer::lmBSE};
  int nmarket = _countof(markets);
  CBondContainer& cont = CBondContainer::instance();
  BondContainerLock _scoped_(cont);
  // 			time_t tNow = cont.GetServerTime();
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const BOND_AUCTION_INFO* opi = (const BOND_AUCTION_INFO*)it.GetRecord();
    if (opi == NULL) continue;
    CBondAuctionInfo info;
    for (int n = 0; n < nmarket; n++) {
      int id = cont.GetBondIndex(opi->Bond_Key, markets[n]);
      if (id >= 0) {
        if (IsFlagDelete(opi->Flag)) {
          cont.RemoveBondAuctionInfo(id);
          break;
        }
        info.SetBondKey(opi->Bond_Key);
        info.SetAuctionTimeStart(opi->Auction_Time_Start);
        info.SetAuctionTimeEnd(opi->Auction_Time_End);
        info.SetPlanedAuctionAmount(opi->Planed_Auction_Amount);
        info.SetActualAuctionAmount(opi->Actual_Auction_Amount);
        info.SetWATenderRate(opi->WA_Tender_Rate);
        info.SetMGTenderRate(opi->MG_Tender_Rate);
        info.SetSubscriptionRatio(opi->Subscription_Ratio);
        info.SetMarginalRatio(opi->Marginal_Ratio);
        cont.AddBondAuctionInfo(id, &info);
      }
    }
  }
  return true;
}
bool UpdateFinancialCompany(RecordEnumerator& fcmap) {
  CSSFinancialCompanyInfo& cont = CSSFinancialCompanyInfo::instance();
  cont.Clear();
  cont.Lock();
  QBFINANCIALCOMPANYINFO info;
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_FINANCIAL_COMPANY* pbl =
        (const QB_FINANCIAL_COMPANY*)it.GetRecord();
    if (pbl == NULL) continue;
    const QB_FINANCIAL_COMPANY& bl = *pbl;
    info.Clear();

    info.m_Name = bl.Name;

    // FIELDCOPY(info.m_CompanyID, bl.CompanyID);
    // info.m_CompanyCode = bl.CompanyCode;
    // info.m_Full_Name = bl.Full_Name;
    // info.m_Name_En = bl.Name_En;
    // info.m_Full_Name_En = bl.Full_Name_En;
    // info.m_City_Name = bl.City_Name;
    if (IsFlagDelete(bl.Flag)) {
      cont.Remove(bl.CompanyID);
    } else {
      cont.Add(bl.CompanyID, info);
    }
  }
  cont.Unlock();
  log_debug(
      "[DICT][CHECK]sizeof(QBFINANCIALCOMPANYINFO)=%6d count:%6d total:%12d",
      sizeof(QBFINANCIALCOMPANYINFO), cont.Size(),
      sizeof(QBFINANCIALCOMPANYINFO) * cont.Size());
  cont.DumpStatus();
  return true;
}
bool UpdateBondDict(RecordEnumerator& fcmap) {
  // SumscopeDB.cpp LoadBondDict
  if (fcmap.IsAllMode())
  {
    CBondDict::instance().Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const BOND_DICT* pbl = (const BOND_DICT*)it.GetRecord();
    if (pbl == NULL) continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const BOND_DICT& bl = *pbl;
    BondDict dict;
    // memset(&dict, 0, sizeof(dict));

    COPY_FIELD(BondDict, dict, selective_code, BOND_DICT, bl, Selective_Code);

    dict.SetSelectiveContent(bl.Selective_Content);
    if (IsEn)
    {
      dict.SetSelectiveContentEn(bl.Selective_Content_EN);
    }

    COPY_FIELD(BondDict, dict, sortcode, BOND_DICT, bl, SortCode);
    COPY_FIELD(BondDict, dict, group_name, BOND_DICT, bl, Group_Name);
    COPY_FIELD(BondDict, dict, group_name_original, BOND_DICT, bl,
               Group_Name_Original);
    COPY_FIELD(BondDict, dict, selective_code_original, BOND_DICT, bl,
               Selective_Code_Original)
    CBondDict::instance().Add(dict);
  }
  CBondDict::instance().DumpStatus();
  return true;
}
bool LoadHoliday(RecordEnumerator& fcmap) {
  // SumscopeDB.cpp LoadHolidaytabe
  if (fcmap.IsAllMode())
  {
    CBondCalendar::instance().Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const BOND_HOLIDAY* pbl = (const BOND_HOLIDAY*)it.GetRecord();
    if (pbl == NULL) continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const BOND_HOLIDAY& bl = *pbl;
    BondHoliday holid;
    memset(&holid, 0, sizeof(holid));
    holid.flag = bl.Flag;
    COPY_FIELD(BondHoliday, holid, country, BOND_HOLIDAY, bl, Country);
    COPY_FIELD(BondHoliday, holid, market, BOND_HOLIDAY, bl, Market);
    COPY_FIELD(BondHoliday, holid, status, BOND_HOLIDAY, bl, Status);
    COPY_FIELD(BondHoliday, holid, HolidayReason, BOND_HOLIDAY, bl,
               HolidayReason);
    CBondCalendar::instance().Add(bl.Holiday, holid);
  }
  CBondCalendar::instance().DumpStatus();
  return true;
}
bool LoadIssueInfo(RecordEnumerator& fcmap) {
  // SumscopeDB.cpp LoadIssuerInfo
  CQBIssuerInfo& icont = CQBIssuerInfo::instance();
  CQBSwSectorInfo& sw = CQBSwSectorInfo::instance2021();
  if (fcmap.IsAllMode())
  {
    icont.Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_ISSUERINFO* pbl = (const QB_ISSUERINFO*)it.GetRecord();
    if (pbl == NULL) continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const QB_ISSUERINFO& bl = *pbl;
    QBISSUERINFO info;
    memset(&info, 0, sizeof(info));

    COPY_FIELD(QBISSUERINFO, info, Institution_Code, QB_ISSUERINFO, bl,
               Institution_Code);
    COPY_FIELD(QBISSUERINFO, info, Issuer_Type, QB_ISSUERINFO, bl, Issuer_Type);
    COPY_FIELD(QBISSUERINFO, info, SW_Sector_Code, QB_ISSUERINFO, bl,
               SW_Sector_Code);
    COPY_FIELD(QBISSUERINFO, info, SW_Subsector_Code, QB_ISSUERINFO, bl,
               SW_Subsector_Code);
    // COPY_FIELD(QBISSUERINFO, info, SW_2021_Sector_Code, QB_ISSUERINFO, bl,
    // SW_Sector_Code_2021);
    COPY_FIELD(QBISSUERINFO, info, Province_Code, QB_ISSUERINFO, bl,
               Province_Code);
    COPY_FIELD(QBISSUERINFO, info, Short_Name_C, QB_ISSUERINFO, bl,
               Short_Name_C);
    COPY_FIELD(QBISSUERINFO, info, Full_Name_C, QB_ISSUERINFO, bl, Full_Name_C);
    if (IsEn)
    {
      COPY_FIELD(QBISSUERINFO, info, Full_Name_E, QB_ISSUERINFO, bl, Full_Name_E);
      COPY_FIELD(QBISSUERINFO, info, Short_Name_E, QB_ISSUERINFO, bl,
        Short_Name_E);
    }
    COPY_FIELD(QBISSUERINFO, info, Listed_Type, QB_ISSUERINFO, bl, Listed_Type);
    COPY_FIELD(QBISSUERINFO, info, Actual_Controller_Name, QB_ISSUERINFO, bl,
               Actual_Controller_Name);
    COPY_FIELD(QBISSUERINFO, info, Actual_Controller_Type, QB_ISSUERINFO, bl,
               Actual_Controller_Type);
    COPY_FIELD(QBISSUERINFO, info, Rate, QB_ISSUERINFO, bl, Rate);
    COPY_FIELD(QBISSUERINFO, info, CBRC_Financing_Platform, QB_ISSUERINFO, bl,
               CBRC_Financing_Platform);
    COPY_FIELD(QBISSUERINFO, info, Municipal_code, QB_ISSUERINFO, bl,
               Municipal_code);
    COPY_FIELD(QBISSUERINFO, info, Issuer_tag, QB_ISSUERINFO, bl, Issuer_tag);

    // 2021版申万行业一二级代码
    const QBSWSECTORINFO* swInfo = sw.GetSwSectorInfo(bl.SW_Sector_Code_2021);
    if (swInfo) {
      if (swInfo->SW_Level == '1') {  // 一级行业
        COPY_FIELD(QBISSUERINFO, info, SW_2021_Sector_Code, QB_ISSUERINFO, bl,
                   SW_Sector_Code_2021);
      } else if (swInfo->SW_Level == '2') {  // 二级行业
        memcpy(info.SW_2021_Sector_Code, swInfo->SW_Upper_Grade_Code,
               sizeof(swInfo->SW_Upper_Grade_Code));
        COPY_FIELD(QBISSUERINFO, info, SW_2021_Subsector_Code, QB_ISSUERINFO,
                   bl, SW_Sector_Code_2021);
      }
    }

    icont.Add(info);
  }
  icont.FinishUpdate();
  icont.DumpStatus();
  return true;
}
bool LoadMainUnderwriter(RecordEnumerator& fcmap) {
  // SumscopeDB.cpp LoadIssuerInfo
  CQBIssuerInfo& icont = CQBIssuerInfo::MainUnderwriterContainer();
  if (fcmap.IsAllMode())
  {
    icont.Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_MAIN_UNDERWRITER* pbl = (const QB_MAIN_UNDERWRITER*)it.GetRecord();
    if (pbl == NULL) continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const QB_MAIN_UNDERWRITER& bl = *pbl;
    QBISSUERINFO info;
    memset(&info, 0, sizeof(info));

    COPY_FIELD(QBISSUERINFO, info, Institution_Code, QB_MAIN_UNDERWRITER, bl,
               Institution_Code);
    COPY_FIELD(QBISSUERINFO, info, Full_Name_C, QB_MAIN_UNDERWRITER, bl,
               Full_Name_C);
    COPY_FIELD(QBISSUERINFO, info, Short_Name_C, QB_MAIN_UNDERWRITER, bl,
               Short_Name_C);
    if (IsEn)
    {
      COPY_FIELD(QBISSUERINFO, info, Full_Name_E, QB_MAIN_UNDERWRITER, bl,
        Full_Name_E);
      COPY_FIELD(QBISSUERINFO, info, Short_Name_E, QB_MAIN_UNDERWRITER, bl,
        Short_Name_E);
    }
    icont.Add(info);
  }
  icont.FinishUpdate();
  icont.DumpStatus();
  return true;
}
bool LoadProvinceInfo(RecordEnumerator& fcmap) {
  // SumscopeDB.cpp LoadProvinceInfo
  const std::string levels = "PRN,CTY";
  if (fcmap.IsAllMode())
  {
    CQBProvinceInfo::instance().Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_PROVINCEINFO* pbl = (const QB_PROVINCEINFO*)it.GetRecord();
    if (pbl == NULL) continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const QB_PROVINCEINFO& bl = *pbl;
    if (levels.find(bl.Area_Level) == std::string::npos)
    {
      continue;
    }
    QBPROVINCEINFO info;

    COPY_FIELD(QBPROVINCEINFO, info, Area_Code, QB_PROVINCEINFO, bl, Area_Code);
    info.SetAreaName(bl.Area_Name);
    if (IsEn)
    {
      info.SetAreaEnglishName(bl.Area_English_Name);
    }
    COPY_FIELD(QBPROVINCEINFO, info, Area_Level, QB_PROVINCEINFO, bl,
               Area_Level);
    COPY_FIELD(QBPROVINCEINFO, info, Area_Upper_Grade_Code, QB_PROVINCEINFO, bl,
               Area_Upper_Grade_Code);
    CQBProvinceInfo::instance().Add(info);
  }
  CQBProvinceInfo::instance().DumpStatus();
  return true;
}
bool LoadSwsectorInfo(RecordEnumerator& fcmap, kDictType type) {
  // SumscopeDB.cpp LoadSwSectorInfo
  CQBSwSectorInfo& swInfo = type == kDictTypeSwsectorInfo
                                ? CQBSwSectorInfo::instance()
                                : CQBSwSectorInfo::instance2021();
  if (fcmap.IsAllMode())
  {
    swInfo.Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_SWSECTORINFO* pbl = (const QB_SWSECTORINFO*)it.GetRecord();
    if (pbl == NULL) continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const QB_SWSECTORINFO& bl = *pbl;
    QBSWSECTORINFO info;
    memset(&info, 0, sizeof(info));
    COPY_FIELD(QBSWSECTORINFO, info, SW_Internal_Code, QB_SWSECTORINFO, bl,
               SW_Internal_Code);
    COPY_FIELD(QBSWSECTORINFO, info, SW_Name, QB_SWSECTORINFO, bl, SW_Name);
    if (IsEn)
    {
      COPY_FIELD(QBSWSECTORINFO, info, SW_English_Name, QB_SWSECTORINFO, bl,
        SW_English_Name);
    }
    COPY_FIELD(QBSWSECTORINFO, info, SW_Upper_Grade_Code, QB_SWSECTORINFO, bl,
               SW_Upper_Grade_Code);
    info.SW_Level = bl.SW_Level;
    swInfo.Add(info);
  }
  swInfo.DumpStatus();
  return true;
}
bool LoadUnderwriterInfo(RecordEnumerator& fcmap) {
  // SumscopeDB.cpp LoadUnderwriterInfo
  if (fcmap.IsAllMode())
  {
    CUnderwriterInfo::instance().Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_UNDERWRITER* pbl = (const QB_UNDERWRITER*)it.GetRecord();
    if (pbl == NULL) continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const QB_UNDERWRITER& bl = *pbl;
    QBUNDERWRITERINFO info;
    memset(&info, 0, sizeof(info));
    COPY_FIELD(QBUNDERWRITERINFO, info, m_UnderwriterUUID, QB_UNDERWRITER, bl,
               UnderwriterUuid);
    COPY_FIELD(QBUNDERWRITERINFO, info, m_UnderwriterCode, QB_UNDERWRITER, bl,
               UnderwriterCode);
    COPY_FIELD(QBUNDERWRITERINFO, info, m_Short_Name_C, QB_UNDERWRITER, bl,
               Short_Name_C);
    info.m_Order = bl.Order;
    CUnderwriterInfo::instance().Add(info);
  }
  CUnderwriterInfo::instance().DumpStatus();
  return true;
}
bool LoadCCFlowInfo(RecordEnumerator& fcmap) {
  // SumscopeDB.cpp _LoadCCFlowInfo
  if (fcmap.IsAllMode())
  {
    CCCFlowManager::instance().Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_CONTINUOUS_CONTRACT_INFO* pbl =
        (const QB_CONTINUOUS_CONTRACT_INFO*)it.GetRecord();
    if (pbl == NULL) continue;
    if (pbl->TFCID[0] == '\0') continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const QB_CONTINUOUS_CONTRACT_INFO& bl = *pbl;
    CCCFlowManager::instance().AddFlowInfo(bl.TFCKey, bl.TFCID, bl.TFCName,
                                           bl.TFID, bl.TFCDate);
  }
  CCCFlowManager::instance().DumpStatus();
  return true;
}
bool LoadExchangerepoInfo(RecordEnumerator& fcmap) {
  // SumscopeDB.cpp _LoadExchangeRepoInfo
  if (fcmap.IsAllMode())
  {
    CExchangeRepoInfo::instance().Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_EXCHANGEREPO_INFO* pbl =
        (const QB_EXCHANGEREPO_INFO*)it.GetRecord();
    if (pbl == NULL) continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const QB_EXCHANGEREPO_INFO& bl = *pbl;
    CExchangeRepoUnit info;
    memset(&info, 0, sizeof(info));
    COPY_FIELD(CExchangeRepoUnit, info, RateKey, QB_EXCHANGEREPO_INFO, bl,
               RateKey);
    COPY_FIELD(CExchangeRepoUnit, info, ID, QB_EXCHANGEREPO_INFO, bl, ID);
    COPY_FIELD(CExchangeRepoUnit, info, Code, QB_EXCHANGEREPO_INFO, bl, Code);
    COPY_FIELD(CExchangeRepoUnit, info, Name, QB_EXCHANGEREPO_INFO, bl, Name);
    COPY_FIELD(CExchangeRepoUnit, info, ListedMarket, QB_EXCHANGEREPO_INFO, bl,
               ListedMarket);
    CExchangeRepoInfo::instance().Add(info);
  }
  CExchangeRepoInfo::instance().DumpStatus();
  return true;
}
bool UpdateBondContainerWithCDCAuth(RecordEnumerator& fcmap) {
  CBondContainer& cont = CBondContainer::instance();
  BondContainerLock _scoped_(cont);
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_BONDCDCAUTH* pbl = (const QB_BONDCDCAUTH*)it.GetRecord();
    if (pbl == NULL) continue;
    const QB_BONDCDCAUTH& bl = *pbl;

    for (int i = 0; i < 3; i++) {
      int nID = cont.GetBondIndex(
          bl.BondKey,
          i == 0 ? CBondContainer::lmCIB
                 : (i == 1 ? CBondContainer::lmSSE : CBondContainer::lmSZE));
      CBondInfo& bi = cont.ElementAtW(
          nID);  // 无效ID会写入CBondContainer::m_bond_null_write

      if (IsFlagDelete(bl.Flag)) {
        bi.SetCdcAuth(-1);
      } else {
        bi.SetCdcAuth(bl.CDCAuth[0] == '\0' ? -1 : atoi(bl.CDCAuth));
      }
    }
  }
  return true;
}
bool LoadCRMBond(RecordEnumerator& fcmap) {
  // SumscopeDB.cpp LoadIssuerInfo
  if (fcmap.IsAllMode())
  {
    QBCRMBond::instance().Clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_BONDCRM* pbl = (const QB_BONDCRM*)it.GetRecord();
    if (pbl == NULL) continue;
    if (IsFlagDelete(pbl->Flag)) continue;
    const QB_BONDCRM& bl = *pbl;
    BOND_CRM_CLAUSE info;
    COPY_FIELD(BOND_CRM_CLAUSE, info, BondKey, QB_BONDCRM, bl, Bond_Key);
    COPY_FIELD(BOND_CRM_CLAUSE, info, ReferenceCode, QB_BONDCRM, bl,
               Reference_Code);
    COPY_FIELD(BOND_CRM_CLAUSE, info, SettlementModes, QB_BONDCRM, bl,
               Settlement_Modes);
    info.RegistrationDate = atoi(bl.Registration_Date);
    info.FirstIssueAmount = bl.First_Issue_Amount;
    info.CreaterCode = bl.Creater_Code;
    info.ReferBondKey = bl.Refer_Bond_Key;
    info.CreditEvent = bl.Credit_Event;
    if (IsEn)
    {
      COPY_FIELD(BOND_CRM_CLAUSE, info, SettlementModesEn, QB_BONDCRM, bl,
        Settlement_ModesEn);
      info.CreditEventEn = bl.Credit_EventEn;
    }
    QBCRMBond::instance().Add(info);
  }
  QBCRMBond::instance().DumpStatus();
  return true;
}

void ConvertToABSBondInfo(const QB_ABSBOND& bi, CABSBondInfo& absinfo,
                          bool isDelete) {
  absinfo.Clear();
  absinfo.SetBondKey(bi.Bond_Key);
  absinfo.SetABSID(bi.ABS_ID);
  if (isDelete) return;
  absinfo.SetABSType(bi.ABS_Type1, bi.ABS_Type2, bi.ABS_Type3);
  absinfo.SetTrancheClass(bi.Tranche_Class);
  absinfo.SetSeriesName(bi.Series_Name);
  absinfo.SetIssuerCode(bi.Issuer_Code);
  absinfo.SetAcutulFinancier(bi.Actual_Financier);
  absinfo.SetCreditSubject(bi.Credit_Subject);
}

void SplitStr(std::vector<std::string>& dst, const std::string& src,
              const std::string seps, bool bCompress = true,
              bool bTotal = false) {
  dst.clear();

  size_t last = 0;
  size_t index = 0;
  if (bTotal == false) {
    index = src.find_first_of(seps, 0);
    while (index != std::string::npos) {
      if (!bCompress || (index - last) > 0) {
        dst.push_back(src.substr(last, index - last));
      }

      last = index + 1;
      index = src.find_first_of(seps, last);
    }
  } else {
    int kLen = seps.length();
    index = src.find(seps, 0);
    while (index != std::string::npos) {
      if (!bCompress || (index - last) > 0) {
        dst.push_back(src.substr(last, index - last));
      }

      last = index + kLen;
      index = src.find(seps, last);
    }
  }

  if (src.length() > last) {
    dst.push_back(src.substr(last));
  }
}

bool UpdateABSBondInfo(RecordEnumerator& fcmap) {
  // 参考CLocalDataUpdate::UpdateBondContainer 和LoadAddIssueTable
  // IsAllMode 为true,表示初次加载
  CABSBondInfo absinfo;
  KeyBuffer combkey(false);
  const char* markets[4] = {CBondContainer::lmCIB, CBondContainer::lmSSE,
                            CBondContainer::lmSZE, CBondContainer::lmBSE};
  int nmarket = _countof(markets);
  CBondContainer& cont = CBondContainer::instance();
  BondContainerLock _scoped_(cont);
  std::set<std::string> setSeries;     // 系列
  std::set<std::string> setFinancier;  // 实际融资人
  std::vector<std::string> vctFinanicer;
  if (fcmap.IsAllMode()) {
    CQBIssuerInfo::ABSBondSeries().Clear();
    CQBIssuerInfo::ABSBondFinancier().Clear();
  }

  if (cont.GetSize() <= 0) {
    return false;
  }

  // 更新BondContainer
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const QB_ABSBOND* pBond = (const QB_ABSBOND*)it.GetRecord();
    if (pBond == NULL) continue;
    const QB_ABSBOND& stAbs = *pBond;
    for (int n = 0; n < nmarket; n++) {
      int id = cont.GetBondIndex(stAbs.Bond_Key, markets[n]);
      if (id >= 0) {
        ConvertToABSBondInfo(stAbs, absinfo, !IsFlagUpdate(stAbs.Flag));
        cont.AddABSBondInfo(id, &absinfo);  // 更新ABSBondInfo
        setSeries.insert(stAbs.Series_Name);
        vctFinanicer.clear();
        SplitStr(vctFinanicer, stAbs.Actual_Financier, ",");
        for (std::vector<std::string>::iterator it = vctFinanicer.begin();
             it != vctFinanicer.end(); ++it) {
          setFinancier.insert(it->c_str());
        }
        break;
      }
    }
  }

  // 更新ABSBondSeries
  CQBIssuerInfo& absIInfo = CQBIssuerInfo::ABSBondSeries();  // 系列
  int nOrder = absIInfo.GetLastOrder();
  char cHead = 0;
  qb::PinyinManager& pyMgr = PinyinManager::instance();
  std::string sJianpin, sQuanpin;
  char cJianpin[32];
  QBISSUERINFO iinfo;
  for (std::set<std::string>::iterator it = setSeries.begin();
       it != setSeries.end(); it++) {
    pyMgr.GetPinyin(it->c_str(), sJianpin, sQuanpin);
    const char* sJ = sJianpin.c_str();
    if (strlen(sJ)) {
      nOrder++;
      cHead = sJ[0];
      FMTBUF(cJianpin, "%c%05d", cHead, nOrder);

      memset(&iinfo, 0, sizeof(QBISSUERINFO));
      FIELDCOPY(iinfo.Institution_Code, cJianpin);
      FIELDCOPY(iinfo.Full_Name_C, it->c_str());
      FIELDCOPY(iinfo.Short_Name_C, it->c_str());
      absIInfo.Add(iinfo, nOrder);
    }
  }
  absIInfo.FinishUpdate();

  // 更新ABSBondFinancier实际融资人
  CQBIssuerInfo& absFinancier = CQBIssuerInfo::ABSBondFinancier();
  CQBIssuerInfo& gIInfo = CQBIssuerInfo::instance();
  for (std::set<std::string>::iterator it = setFinancier.begin();
       it != setFinancier.end(); it++) {
    const IssuerInfo* pIssuer = gIInfo.GetIssuerInfo(*it);
    if (!pIssuer) continue;

    memset(&iinfo, 0, sizeof(QBISSUERINFO));
    FIELDCOPY(iinfo.Institution_Code, it->c_str());
    FIELDCOPY(iinfo.Full_Name_C, pIssuer->field((int)kIssuerInfo::Full_Name_C));
    if (IsEn)
    {
      FIELDCOPY(iinfo.Full_Name_E, pIssuer->field((int)kIssuerInfo::Full_Name_E));
    }
    absFinancier.Add(iinfo);
  }
  absFinancier.FinishUpdate();
  return true;
}

//////////////////////////////////////////////////////////////////////////
/*	bool UpdateBondConversion(RecordEnumerator& fcmap)
        {
                CBondContainer& cont = AfxGetBondContainer();
                BondContainerLock _scoped_(cont);
                if (fcmap.IsAllMode())
                {
                        for (RecordEnumerator::Iterator it = fcmap.begin(); it
   != fcmap.end(); ++it)
                        {
                                const BOND_CONVERSION* pBondConv = (const
   BOND_CONVERSION*)it.GetRecord(); if (pBondConv == NULL)continue; const
   BOND_CONVERSION& bc = *pBondConv;

                                if (IsFlagDelete(bc.Flag)) continue;
                                int id = cont.GetBondIndex(bc.ComBondKey);
                                if (cont.IsValidIndex(id))
                                {
                                        CBondInfo& bi = cont.ElementAtW(id);
                                        bi.SetConversionRate(bc.ConversionRate);
                                }
                        }
                        return true;
                }
                if (cont.GetSize() <= 0) {
                        return false;
                }

                for (RecordEnumerator::Iterator it = fcmap.begin(); it !=
   fcmap.end(); ++it)
                {
                        const BOND_CONVERSION* pBondConv = (const
   BOND_CONVERSION*)it.GetRecord(); if (pBondConv == NULL)continue; const
   BOND_CONVERSION& bc = *pBondConv; int id = cont.GetBondIndex(bc.ComBondKey);
                        if (!cont.IsValidIndex(id))continue;

                        CBondInfo& bi = cont.ElementAtW(id);
                        if (IsFlagUpdate(bc.Flag))
                        {
                                bi.SetConversionRate(bc.ConversionRate);
                        }
                        else
                        {
                                bi.SetConversionRate("");
                        }
                }
                return true;
        }

        bool UpdateBondDefaulted(RecordEnumerator& fcmap)
        {
                std::string comBondKey = "";
                const std::vector<std::string> listMarkets = {
   CBondContainer::lmCIB, CBondContainer::lmSSE, CBondContainer::lmSZE,
   CBondContainer::lmBSE }; CBondContainer& cont = AfxGetBondContainer();
                BondContainerLock _scoped_(cont);
                if (fcmap.IsAllMode())
                {
                        for (RecordEnumerator::Iterator it = fcmap.begin(); it
   != fcmap.end(); ++it)
                        {
                                const BOND_DEFAULTED* pBondConv = (const
   BOND_DEFAULTED*)it.GetRecord(); if (pBondConv == NULL)continue; const
   BOND_DEFAULTED& bc = *pBondConv; if (IsFlagDelete(bc.Flag)) continue;

                                for (size_t i = 0; i < listMarkets.size(); i++)
   { comBondKey = bc.ComBondKey; comBondKey += "."; comBondKey +=
   listMarkets[i]; int id = cont.GetBondIndex(comBondKey); if
   (cont.IsValidIndex(id))
                                        {
                                                CBondInfo& bi =
   cont.ElementAtW(id); bi.SetDefaulted(true);
                                        }
                                }
                        }
                        return true;
                }
                if (cont.GetSize() <= 0) {
                        return false;
                }

                for (RecordEnumerator::Iterator it = fcmap.begin(); it !=
   fcmap.end(); ++it)
                {
                        const BOND_DEFAULTED* pBondConv = (const
   BOND_DEFAULTED*)it.GetRecord(); if (pBondConv == NULL)continue; const
   BOND_DEFAULTED& bc = *pBondConv;

                        for (size_t i = 0; i < listMarkets.size(); i++) {
                                comBondKey = bc.ComBondKey;
                                comBondKey += ".";
                                comBondKey += listMarkets[i];

                                int id = cont.GetBondIndex(comBondKey);
                                if (!cont.IsValidIndex(id))continue;

                                CBondInfo& bi = cont.ElementAtW(id);
                                if (IsFlagUpdate(bc.Flag))
                                {
                                        bi.SetDefaulted(true);
                                }
                                else
                                {
                                        bi.SetDefaulted(false);
                                }
                        }
                }
                return true;
        }

        bool UpdateKongfuBondInfo(RecordEnumerator& fcmap)
        {
                for (RecordEnumerator::Iterator it = fcmap.begin(); it !=
   fcmap.end(); ++it) { const BOND_KONGFU* pbl = (const
   BOND_KONGFU*)it.GetRecord(); if (pbl == nullptr || IsFlagDelete(pbl->Flag))
                                continue;

                        const BOND_KONGFU& bl = *pbl;
                        CKongfuBond* bond = AfxGetKongfuBond().AddBond();
                        if (bond) {
                                bond->SetISINCode(bl.ISIN_Code);
                                bond->SetBondName(bl.Short_Name_C);
                                bond->SetIssuerName(bl.Issuer_Name);
                                bond->SetLowestRating(bl.Lowest_Rating);
                                bond->SetNextCallDate(bl.Next_Call_Date);
                                bond->SetNextPutDate(bl.Next_Put_Date);
                                bond->SetInterestAccrualDate(bl.Interest_Accrual_Date);
                                bond->SetMaturityDate(bl.Maturity_Date);
                                bond->SetOutstandingAmount(atof(bl.Outstanding_Amount));
                                bond->SetIssuedAmount(atof(bl.Issued_Amount));
                        }
                }
                return true;
        }
*/
bool UpdateSpecialLabelInfo(RecordEnumerator& fcmap) {
    if (fcmap.IsAllMode())
    {
      SpecialLabelMgr::instance().clear();
    }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const SPECAIL_LABEL_INFO* pbl = (const SPECAIL_LABEL_INFO*)it.GetRecord();
    if (pbl == nullptr) {
      continue;
    }

    if (IsFlagDelete(pbl->Flag)) {
      SpecialLabelMgr::instance().remove(pbl->id);
      continue;
    }

    SpecialLabel label;
    label.setId(pbl->id);
    label.SetProperty(pbl->property);
    label.SetLabel(pbl->label);
    label.SetColor(pbl->color);
    label.SetComment(pbl->comments);
    label.SetOrder(pbl->label_order);
    SpecialLabelMgr::instance().add(label);
  }
  return true;
}

bool UpdateBondSpecialLabel(RecordEnumerator& fcmap) {
  char combBondKey[40] = {0};
  char labelId[16] = {0};
  BondSpecialLabelMgr& bslm = BondSpecialLabelMgr::instance();
  if (fcmap.IsAllMode())
  {
    bslm.clear();
  }
  for (RecordEnumerator::Iterator it = fcmap.begin(); it != fcmap.end(); ++it) {
    const BOND_SPECIAL_LABEL* pbl = (const BOND_SPECIAL_LABEL*)it.GetRecord();
    if (pbl == nullptr) {
      continue;
    }

    const char* dotstr = strrchr(pbl->bond_label, '.');
    int dot = dotstr ? (dotstr - pbl->bond_label) : (-1);
    if (dot <= 0) {
      continue;
    }

    memset(combBondKey, 0, sizeof(combBondKey));
    memset(labelId, 0, sizeof(labelId));
    FIELDCOPYN(combBondKey, dot + 1, pbl->bond_label);
    FIELDCOPY(labelId, pbl->bond_label + dot + 1);

    if (IsFlagUpdate(pbl->Flag)) {
      bslm.addLabel(combBondKey, labelId, pbl->label_tip, pbl->label_order);
    } else {
      bslm.removeLabel(combBondKey, labelId);
    }
  }
  return true;
}

//////////////////////////////////////////////////////////////////////////
void DictLoaderListener::LoadRecords(RecordEnumerator& enumerator,
                                     kDictType type) {
  /*

  参考CLocalDataUpdate::FinishUpdate,
  每次请求更新后,有如下种类需要更新
  kDictTypeFinancialCompany
  kDictTypeBondList
  kDictTypeBondPrimaryInfo

  此处将记录从磁盘结构体形式转化为内存结构体形式,更新到对应的容器中
  */
  switch (type) {
    case kDictTypeBondList:
      UpdateBondContainerWithBondList(enumerator);
      break;
    case kDictTypeBondPrimaryInfo:
      UpdateBondContainerWithPrimaryInfo(enumerator);
      break;
    case kDictTypeBondAdditionInfo:
      UpdateBondContainerWithAddtionInfo(enumerator);
      break;
    case kDictTypeBondOptionInfo:
      UpdateBondOptionInfo(enumerator);
      break;
    case kDictTypeBondAuctionInfo:
      UpdateBondAuctionInfo(enumerator);
      break;
    case kDictTypeABSBond:
      UpdateABSBondInfo(enumerator);
      break;
    case kDictTypeBondCDCAuth:
      UpdateBondContainerWithCDCAuth(enumerator);
      break;
    case kDictTypeBondDict:
      UpdateBondDict(enumerator);
      break;
    case kDictTypeBondHoliday:
      LoadHoliday(enumerator);
      break;
    case kDictTypeIssuerInfo:
      LoadIssueInfo(enumerator);
      break;
    case kDictTypeMainUnderwriter:
      LoadMainUnderwriter(enumerator);
      break;
    case kDictTypeProvinceInfo:
      LoadProvinceInfo(enumerator);
      break;
    case kDictTypeSwsectorInfo:
    case kDictTypeSwsectorInfo2021:
      LoadSwsectorInfo(enumerator, type);
      break;
    case kDictTypeUnderwriter:
      LoadUnderwriterInfo(enumerator);
      break;
    case kDictTypeFinancialCompany:
      //UpdateFinancialCompany(enumerator);
      break;
    case kDictTypeContinuousContractInfo:
      LoadCCFlowInfo(enumerator);
      break;
    case kDictTypeExchangeRepo:
      LoadExchangerepoInfo(enumerator);
      break;
    case kDictTypeBondCRMClause:
      LoadCRMBond(enumerator);
      break;
      //	case kDictTypeBondKongfu:UpdateKongfuBondInfo(enumerator);
      //break;
    case kDictTypeSpecialLabelInfo:
      UpdateSpecialLabelInfo(enumerator);
      break;
    case kDictTypeBondSpecialLabel:
      UpdateBondSpecialLabel(enumerator);
      break;
    default:
      BASSERT(0);
      break;
  }
}

void DictLoaderListener::SetLanguage(bool en)
{
  IsEn = en;
}
}  // namespace ht
}  // namespace qb
